package com.itaming.lycheeframework.support.datetime;

import com.itaming.lycheeframework.common.constant.FrameworkConstant;
import org.springframework.lang.NonNull;

import java.text.*;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

/**
 * 灵活的 {@code Date} 格式化器
 *
 * @author A.Ming
 */
public class FlexibleDateFormat extends SimpleDateFormat {

    /**
     * 构造函数
     */
    public FlexibleDateFormat() {
        super(FrameworkConstant.DATETIME_FORMAT);
    }

    /**
     * 构造函数
     *
     * @param pattern 日期时间格式
     */
    public FlexibleDateFormat(String pattern) {
        super(pattern);
    }

    /**
     * 构造函数
     *
     * @param pattern 日期时间格式
     * @param locale  语言环境
     */
    public FlexibleDateFormat(String pattern, Locale locale) {
        super(pattern, locale);
    }

    /**
     * 构造函数
     *
     * @param pattern       日期时间格式
     * @param formatSymbols 格式符号
     */
    public FlexibleDateFormat(String pattern, DateFormatSymbols formatSymbols) {
        super(pattern, formatSymbols);
    }

    /**
     * Formats a {@link Date} into a date-time string. The formatted
     * string is appended to the given {@code StringBuffer}.
     *
     * @param date          a Date to be formatted into a date-time string.
     * @param toAppendTo    the string buffer for the returning date-time string.
     * @param fieldPosition keeps track on the position of the field within
     *                      the returned string. For example, given a date-time text
     *                      {@code "1996.07.10 AD at 15:08:56 PDT"}, if the given {@code fieldPosition}
     *                      is {@link DateFormat#YEAR_FIELD}, the begin index and end index of
     *                      {@code fieldPosition} will be set to 0 and 4, respectively.
     *                      Notice that if the same date-time field appears more than once in a
     *                      pattern, the {@code fieldPosition} will be set for the first occurrence
     *                      of that date-time field. For instance, formatting a {@code Date} to the
     *                      date-time string {@code "1 PM PDT (Pacific Daylight Time)"} using the
     *                      pattern {@code "h a z (zzzz)"} and the alignment field
     *                      {@link DateFormat#TIMEZONE_FIELD}, the begin index and end index of
     *                      {@code fieldPosition} will be set to 5 and 8, respectively, for the
     *                      first occurrence of the timezone pattern character {@code 'z'}.
     * @return the string buffer passed in as {@code toAppendTo}, with formatted
     * text appended.
     */
    @Override
    public StringBuffer format(@NonNull Date date, @NonNull StringBuffer toAppendTo, @NonNull FieldPosition fieldPosition) {
        return super.format(date, toAppendTo, fieldPosition);
    }

    /**
     * Parse a date/time string according to the given parse position.  For
     * example, a time text {@code "07/10/96 4:5 PM, PDT"} will be parsed into a {@code Date}
     * that is equivalent to {@code Date(837039900000L)}.
     *
     * <p> By default, parsing is lenient: If the input is not in the form used
     * by this object's format method but can still be parsed as a date, then
     * the parse succeeds.  Clients may insist on strict adherence to the
     * format by calling {@link #setLenient(boolean) setLenient(false)}.
     *
     * <p>This parsing operation uses the {@link #calendar} to produce
     * a {@code Date}. As a result, the {@code calendar}'s date-time
     * fields and the {@code TimeZone} value may have been
     * overwritten, depending on subclass implementations. Any {@code
     * TimeZone} value that has previously been set by a call to
     * {@link #setTimeZone(TimeZone) setTimeZone} may need
     * to be restored for further operations.
     *
     * @param source The date/time string to be parsed
     * @param pos    On input, the position at which to start parsing; on
     *               output, the position at which parsing terminated, or the
     *               start position if the parse failed.
     * @return A {@code Date}, or {@code null} if the input could not be parsed
     */
    @Override
    public Date parse(@NonNull String source, ParsePosition pos) {
        try {
            Date date = DateTimeParser.parseDate(source);
            pos.setIndex(source.length());
            return date;
        } catch (ParseException e) {
            pos.setErrorIndex(pos.getIndex());
            return null;
        }
    }

}
